package com.nulldev.util.web.HttpServer3.extensions.nCaptcha.captchas.advanced;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;

import com.nulldev.util.VariableAPI.RandomUtil;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.data.Images.ColorUtil;
import com.nulldev.util.data.Images.ImageUtil;
import com.nulldev.util.data.Images.legacy.TextObf;
import com.nulldev.util.data.Sets.DataSet;
import com.nulldev.util.gui.swingAPI.Swing;
import com.nulldev.util.web.HttpServer3.extensions.nCaptcha.backend.Captcha;
import com.nulldev.util.web.HttpServer3.pages.v3.HttpPage3.Request;

public class ImageTextCaptchaV3 implements Captcha {

	private static final int CANVAS_WIDTH = 320;
	private static final int CANVAS_HEIGHT = 240;

	private static final int EDGE_OFFSET = 40;

	@Override
	public DataSet<String, String[]> generateBase64Image(final int botLevel, final Request request) {
		final String word = StringsUtil.randomString(6);
		final BufferedImage pre = ImageUtil.createFromColorImg(Color.WHITE, CANVAS_WIDTH, CANVAS_HEIGHT);
		final Graphics g = pre.getGraphics();
		((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, Swing.autoAAHint());
		// Create a obfuscated background
		if (botLevel <= 1) {
			final Point center = new Point(160, 120);
			float radius = 200;
			float[] dist =
				{ 0f, 1f };
			final Color[] colors =
				{ ColorUtil.randomColor(), ColorUtil.randomColor() };
			drawBackGroundCircle((Graphics2D) g, radius, Color.WHITE, center);
			drawGradientCircle((Graphics2D) g, radius, dist, colors, center);
		} else {
			g.setColor(ColorUtil.rainbow(200, .4f));
			g.fillRect(0, 0, 80, 240);
			g.setColor(ColorUtil.rainbow(1200, .8f));
			g.fillRect(80, 0, 80, 240);
			g.setColor(ColorUtil.rainbow(2400, .9f));
			g.fillRect(160, 0, 80, 240);
			g.setColor(ColorUtil.rainbow(2400, .2f));
			g.fillRect(240, 0, 80, 240);
		}
		// Create layered background text
		final Font font = TextObf.__rand_font(20);
		final AffineTransform affineTransform = new AffineTransform();
		final int angle = RandomUtil.randInt(-50, -40);
		affineTransform.rotate(Math.toRadians(angle), 0, 0);
		final Font rotatedFont = font.deriveFont(affineTransform);
		g.setFont(rotatedFont);
		for (int i = 0; i < 24; i++) {
			final String randomLine = StringsUtil.randomStringAlphabet(48);
			final Color clr = ColorUtil.randomColorMaxAlpha(222);
			g.setColor(clr);
			g.drawString(randomLine, 0, 600 - (i * 24));
		}
		g.setFont(font);
		// Draw obfuscated challenge word over.
		int _offsetX = RandomUtil.randInt(EDGE_OFFSET, CANVAS_WIDTH - EDGE_OFFSET);
		final int strLen = g.getFontMetrics().stringWidth(word);
		if (_offsetX + strLen >= (CANVAS_WIDTH - EDGE_OFFSET)) {
			_offsetX -= strLen;
		}
		final int offsetX = _offsetX;
		final int offsetY = RandomUtil.randInt(EDGE_OFFSET, CANVAS_HEIGHT - EDGE_OFFSET);
		{
			((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
			final String s = word;
			FontRenderContext frc = ((Graphics2D) g).getFontRenderContext();
			g.translate(offsetX, offsetY);

			final GlyphVector gv = font.createGlyphVector(frc, s);
			int length = gv.getNumGlyphs();
			g.setColor(Color.black);
			for (int i = 0; i < length; i++) {
				final Point2D p = gv.getGlyphPosition(i);
				final double theta = (double) i / (double) (length - 1) * Math.PI / 4;
				final AffineTransform at = AffineTransform.getTranslateInstance(p.getX() - 1, p.getY() - 1);
				at.rotate(theta);
				final Shape glyph = gv.getGlyphOutline(i);
				final Shape transformedGlyph = at.createTransformedShape(glyph);
				((Graphics2D) g).fill(transformedGlyph);
			}
			g.setColor(ColorUtil.randomColor(255));
			for (int i = 0; i < length; i++) {
				final Point2D p = gv.getGlyphPosition(i);
				final double theta = (double) i / (double) (length - 1) * Math.PI / 4;
				final AffineTransform at = AffineTransform.getTranslateInstance(p.getX(), p.getY());
				at.rotate(theta);
				final Shape glyph = gv.getGlyphOutline(i);
				final Shape transformedGlyph = at.createTransformedShape(glyph);
				((Graphics2D) g).fill(transformedGlyph);
			}
		}
		final String base64 = ImageUtil.encodeToString(pre, "png");
		return new DataSet<String, String[]>("data:image/png;base64," + base64, new String[]
			{ word });
	}

	private static void drawBackGroundCircle(final Graphics2D g2d, float radius, final Color color, final Point2D center) {
		g2d.setColor(color);
		radius -= 1;
		g2d.fill(new Ellipse2D.Double(center.getX() - radius, center.getY() - radius, radius * 2, radius * 2));
	}

	private static void drawGradientCircle(final Graphics2D g2d, final float radius, final float[] dist, final Color[] colors, final Point2D center) {
		final RadialGradientPaint rgp = new RadialGradientPaint(center, radius, dist, colors);
		g2d.setPaint(rgp);
		g2d.fill(new Ellipse2D.Double(center.getX() - radius, center.getY() - radius, radius * 2, radius * 2));
	}

	@Override
	public String getType() {
		return "screenwords3";
	}
}
